fix(workflows): handle an unreadable run state in workflow status - #3999
Merged
Conversation
`workflow status <run_id>` and `workflow resume <run_id>` both call `RunState.load()`, and a prior fix aligned them on the FileNotFoundError and ValueError boundaries. `resume` also handles OSError; `status` never gained that handler. So an unreadable `state.json` -- wrong permissions, an I/O error, or a directory sitting where the file belongs -- escapes as a raw traceback with no output at all, while `resume` on the same run prints a clean `Error:` line and exits 1. `state_path.exists()` is True for a directory, so the existing guard passes and `open()` raises. Add the missing `except OSError` next to its siblings, using the same `_escape_markup` + `typer.Exit(1)` shape, and routing through `err` so the message lands on stderr under `--json` and the stdout JSON stream stays parseable. Two regression tests: the end-to-end CLI path (a directory in place of state.json) and the `--json` stderr-routing path. Both fail without the source change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem
workflow status <run_id>andworkflow resume <run_id>both load the same run state viaRunState.load(). A prior fix aligned the two on theFileNotFoundErrorandValueErrorboundaries, butresumealso handlesOSErrorandstatusnever gained that handler:So an unreadable
state.json— wrong permissions, an I/O error, or a directory sitting where the file belongs — escapesworkflow statusas a raw traceback, whileresumeon the very same run exits cleanly.state_path.exists()returns True for a directory, so the existing guard passes andopen()raises.Reproduction
With a run directory whose
state.jsonis a directory, on unmodifiedmain:statusprints nothing at all and dumps a traceback;resumeprints a clean error. Reproduced through the real Typer CLI, no mocking.Fix
Add the missing
except OSErrorbeside its siblings, using the same_escape_markup+typer.Exit(1)shape as the neighbouring handlers, and routing througherrso the message goes to stderr under--jsonand the stdout JSON stream stays parseable.Tests
Two regression tests in
TestWorkflowCliAlignment, next to the existingstatus/resumealignment tests:test_status_unreadable_run_state_exits_cleanly— end-to-end CLI, a directory in place ofstate.jsontest_status_json_unreadable_run_state_error_goes_to_stderr—--jsonstderr routing, mirroring the siblingValueErrortestBoth fail without the source change (verified by reverting
_commands.pyalone) and pass with it.tests/test_workflows.py: 898 passed, up from 896 on the same tree, with an unchanged set of 20 pre-existing failures — all Windows symlink tests that need elevation and fail identically on unmodifiedmain.🤖 Generated with Claude Code